﻿using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc.Formatters;
using Microsoft.AspNetCore.WebUtilities;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using System;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using NetMicro.Core.ApiFunction;
using NetMicro.Core.Extensions;
using NetMicro.Validations.Exceptions;

namespace NetMicro.Webs.Middlewares
{

    public class RequestBodyMiddleware
    {

        private readonly RequestDelegate _next;
        private readonly ILogger<RequestBodyMiddleware> _logger;
        private readonly IWebHostEnvironment _env;
        public RequestBodyMiddleware(RequestDelegate next, ILogger<RequestBodyMiddleware> logger, IWebHostEnvironment env)
        {
            _next = next;
            _logger = logger;
            _env = env;

        }
        public async Task Invoke(HttpContext context)
        {
            Stopwatch watch = Stopwatch.StartNew();
            await LogRequest(context);
            await LogResponse(context);
            watch.Stop();
            _logger.LogInformation($"Request Time (MS):{ watch.ElapsedMilliseconds}");
        }

        protected virtual async Task LogRequest(HttpContext context)
        {
            _logger.LogInformation($"Http Request Information:{Environment.NewLine}" +
                                       $"Schema:{context.Request.Scheme} " +
                                       $"Host: {context.Request.Host} " +
                                       $"Path: {context.Request.Path} " +
                                       $"QueryString: {context.Request.QueryString} " +
                                       $"Request Body: {await ReadBodyAsync(context.Request)}");

        }
       

        private async Task<string> ReadBodyAsync(HttpRequest request)
        {
            if (null != request.ContentType && request.ContentType.Contains("boundary="))
            {
                await EnableRewindAsync(request);
            }
            else
            {
                if (request.ContentLength > 0)
                {
                    await EnableRewindAsync(request);
                    Encoding encoding = GetRequestEncoding(request);
                    return await this.ReadStreamAsync(request.Body, encoding).ConfigureAwait(false);
                }
            }
            return null;
        }

        private Encoding GetRequestEncoding(HttpRequest request)
        {
            string requestContentType = request.ContentType;
            MediaType requestMediaType = requestContentType == null ? default : new MediaType(requestContentType);
            Encoding requestEncoding = requestMediaType.Encoding;
            if (requestEncoding == null)
            {
                requestEncoding = Encoding.UTF8;
            }
            return requestEncoding;
        }

        private async Task EnableRewindAsync(HttpRequest request)
        {
            if (!request.Body.CanSeek)
            {
                request.EnableBuffering();

                await request.Body.DrainAsync(CancellationToken.None);
                request.Body.Seek(0L, SeekOrigin.Begin);
            }
        }

        private async Task<string> ReadStreamAsync(Stream stream, Encoding encoding)
        {
            using StreamReader sr = new StreamReader(stream, encoding, true, 1024, true);//这里注意Body部分不能随StreamReader一起释放
            string str = await sr.ReadToEndAsync();
            stream.Seek(0, SeekOrigin.Begin);//内容读取完成后需要将当前位置初始化，否则后面的InputFormatter会无法读取
            return str;
        }


        protected virtual async Task LogResponse(HttpContext context)
        {
            using MemoryStream ms = new MemoryStream();
            Stream originalBodyStream = context.Response.Body;
            context.Response.Body = ms;
            try
            {

                await _next.Invoke(context);
                ms.Position = 0;
                StreamReader reader = new StreamReader(ms);
                _logger.LogInformation($"Http Response Information:{Environment.NewLine}" +
                                   $"Schema:{context.Request.Scheme} " +
                                   $"Host: {context.Request.Host} " +
                                   $"Path: {context.Request.Path} " +
                                   $"QueryString: {context.Request.QueryString} " +
                               $"Response Body: { await reader.ReadToEndAsync()}");

                ms.Position = 0;
                await ms.CopyToAsync(originalBodyStream);
                context.Response.Body = originalBodyStream;

            }
            catch (Exception ex)
            {
                byte[] data = Encoding.UTF8.GetBytes(ExceptionReturnResult(ex));
                await originalBodyStream.WriteAsync(data, 0, data.Length);
            }
            finally
            {
                context.Response.Body = originalBodyStream;
            }
        }

        protected virtual string ExceptionReturnResult(Exception ex)
        {
            if (ex == null)
                return default;
            _logger.LogError($"全局异常拦截----异常结果输出: {ex}");
            if (ex is ValidationException validation)
                return ServiceResult.Faild<string>(validation.Code, validation.Message).ToJson();
            return ServiceResult.Faild<string>(StatusCodes.Status500InternalServerError, $"{(!_env.IsDevelopment() ? "服务器开小差了,请稍后再试" : ex?.Message)}").ToJson();
        }
    }
}